<!--
 * @Author: 赵宇
 * @Description: 流程编辑
 * @Date: 2023-02-09 13:40:57
 * @LastEditTime: 2023-02-23 15:17:24
 * @LastEditors: zhao 13370229059@163.com
 * @FilePath: \pcj\src\page\flow\components\flowEdit.vue
-->
<template>
  <div class="flow">
    <div class="dis-flex flex-y-center pt-10 ml-10">
      <z-button icon="check" @click="onCheck"> 确认 </z-button>
      <el-input v-model="state.progressName" ref="progressName" placeholder="流程名称" class="width-200 ml-10"></el-input>
      <el-switch v-model="state.isAutoLine" :active-value="true" :inactive-value="false" class="ml-10 mr-5"> </el-switch>
      <el-link type="primary" :underline="false">是否自动连线</el-link>
    </div>
    <div class="flow-super">
      <el-scrollbar always>
        <SuperFlow
          :style="{
            height: state.Height + 'px',
            width: state.Width + 'px'
          }"
          ref="superFlow"
          :node-list="params.nodeList"
          :link-list="params.linkList"
          :graph-menu="params.graphMenuList"
          :node-menu="params.nodeMenuList"
          :link-menu="params.linkMenuList"
          :enter-intercept="params.enterIntercept"
          :output-intercept="params.outputIntercept"
        >
          <template v-slot:node="{ meta }">
            <div :class="`flow-node flow-node-${meta.prop}`" :ref="'flow_' + meta.id">
              <header v-if="['end', 'start'].includes(meta.prop)">{{ meta.taskName }}</header>
              <el-input v-model="meta.taskName" v-else :class="`flow-node-input-${meta.prop}`"></el-input>
              <section v-if="!['end', 'start'].includes(meta.prop)">
                <el-select v-model="meta.taskType">
                  <el-option v-for="item in taskOptions" :key="item.value" :label="item.label" :value="item.value"></el-option>
                </el-select>
              </section>
            </div>
          </template>
          <template v-slot:menuItem="meta">
            <span class="super-flow__menu-item-content" v-if="meta.item.disabled">{{ meta.item.label }}</span>
          </template>
        </SuperFlow>
      </el-scrollbar>
      <div class="flow-super-bottom" @click="onAddPx('Height')">
        <z-button type="success" plain icon="add" circle></z-button>
      </div>
      <div class="flow-super-right" @click="onAddPx('Width')">
        <z-button type="success" plain icon="add" circle></z-button>
      </div>
    </div>
    <nodeEdit v-model="state.nodeShow" :editData="state.editData" :nodeList="state.nodeList" @onSave="onSave" />
  </div>
</template>

<script setup>
import nodeEdit from '@/page/flow/components/nodeEdit.vue';
import { flowNodeDefault } from '@/page/flow/flow.js';
import { SuperFlow } from 'vue3-super-flow';
import { taskOptions } from '@/config/data';
import http from '@/api/index';
import 'vue3-super-flow/dist/style.css';
let { hash } = inject('$global');
let route = useRoute();
let router = useRouter();
let $message = inject('$message');
let nodeId = ref(1000);
let superFlow = ref();
let progressName = ref();
let getOnlyId = () => {
  return ++nodeId.value;
};
let urlParams = computed(() => route.query);
// 流程值处理
let params = reactive({
  nodeList: [],
  linkList: [],
  graphMenuList: [
    [
      {
        label: '开始节点',
        disable(graph) {
          return !!graph.nodeList.find(node => node.meta.prop === 'start');
        },
        selected: (graph, coordinate) => {
          const id = getOnlyId();
          let obj = {
            id,
            width: 70,
            height: 40,
            coordinate,
            meta: flowNodeDefault({
              id,
              prop: 'start',
              taskName: '开始',
              taskType: 0
            })
          };
          graph.addNode(obj);
        }
      },
      {
        label: '添加节点',
        disable: false,
        selected: (graph, coordinate) => {
          const id = getOnlyId();
          let obj = {
            id,
            width: 200,
            height: 100,
            coordinate,
            meta: flowNodeDefault({
              id,
              prop: 'approval',
              taskName: '节点' + (nodeId.value - 1000),
              taskType: 1
            })
          };
          graph.addNode(obj);
          onAutoLine(id);
        }
      },
      {
        label: '完成节点',
        disable(graph) {
          return !!graph.nodeList.find(point => point.meta.prop === 'end');
        },
        selected: (graph, coordinate) => {
          const id = getOnlyId();
          let obj = {
            id,
            width: 70,
            height: 40,
            coordinate, // 坐标
            meta: flowNodeDefault({
              id,
              prop: 'end',
              taskName: '完成',
              taskType: 999
            })
          };
          graph.addNode(obj);
          onAutoLine(id);
        }
      }
    ]
  ],
  nodeMenuList: [
    [
      {
        label: '编辑',
        disable: graph => {
          // 修改的时候
          if (['start', 'end'].includes(graph.meta.prop)) {
            return true;
          }
          return false;
        },
        selected: node => {
          let { nodeList } = node.graph;
          console.log(nodeList, node);
          state.editData = node.meta;
          // 这里处理掉 开始 结束节点，省的传递子组件要处理
          state.nodeList = nodeList.map(r => r.meta).filter(r => ![0, 999].includes(r.taskType));
          state.nodeShow = true;
        }
      },

      {
        label: '删除',
        disable: graph => {
          return ['start', 'end'].includes(graph.meta.prop);
        },
        selected: (node, coordinate) => {
          node.remove();
        }
      }
    ]
  ],
  linkMenuList: [
    [
      {
        label: '删除',
        disable: false,
        selected(link, coordinate) {
          link.remove();
        }
      }
    ]
  ],
  // 连续结束限制。
  enterIntercept: (formNode, toNode, graph) => {
    const formType = formNode.meta.prop;
    switch (toNode.meta.prop) {
      case 'start':
        return false;
      case 'approval':
        return ['start', 'approval'].includes(formType);
      case 'end':
        return ['approval'].includes(formType);
      default:
        return true;
    }
  },
  // 连线限制，只要不是结束节点，都可以开始连线
  outputIntercept: (node, graph) => {
    return !(node.meta.prop === 'end');
  }
});
let state = reactive({
  nodeShow: false,
  editData: {},
  nodeList: [],
  progressName: '',
  isAutoLine: true,
  Height: 100,
  Width: 200
});
// 点击保存
const onCheck = () => {
  let type = false;
  if (!superFlow.value) return;
  let graph = superFlow.value.graph.toJSON();
  let { nodeList, linkList } = graph;
  // 筛出 开始、结束节点 筛出 非开始、结束节点
  let nodeResult = [nodeList.filter(r => [0, 999].includes(r.meta.taskType)), nodeList.filter(r => ![0, 999].includes(r.meta.taskType))];
  // 必须具备开始 和 结束 节点 。 并且起码要有一个 任务节点
  type = nodeResult[0].length === 2 && nodeResult[1].length;
  // type 为真，才有必要判断线对不对
  if (type) {
    // 开始和结束 必须都要有连线
    let likeResult = [
      linkList.filter(r => r.startId === nodeResult[0].filter(n => n.meta.taskType === 0)[0].id),
      linkList.filter(r => r.endId === nodeResult[0].filter(n => n.meta.taskType === 999)[0].id)
    ];
    type = likeResult[0].length && likeResult[1].length; // 都可以存在多条分支分出去 ,也可以存在多条链接结束
  }
  if (type) {
    let arr = nodeList.filter(r => ![0, 999].includes(r.meta.taskType));
    // 判断除了开始 和 结束 ，其他节点 都必须要有开始 和结束 二种连线
    arr.map(r => {
      // 如果为 false 的就不要往下执行了！
      if (!type) return;
      let obtain = [linkList.filter(n => n.startId === r.id), linkList.filter(n => n.endId === r.id)];
      type = obtain[0].length && obtain[1].length;
    });
  }
  if (!type) {
    $message.warning('流程错误，请检查流程图配置!');
    return;
  }
  // 如果是采购单 必须要有一个采购入库节点。还只能有一个
  if (Number(urlParams.value.type) === 1) {
    if (nodeList.filter(r => r.meta.taskType === 3).length !== 1) {
      $message.warning('采购单必须要有采购入库节点，有且只能有一个!');
      return;
    }
  }
  // 如果是业务单 必须要有一个发货节点。还只能有一个
  if (Number(urlParams.value.type) === 2) {
    if (nodeList.filter(r => r.meta.taskType === 5).length !== 1) {
      $message.warning('业务单必须要有发货节点，有且只能有一个!');
      return;
    }
  }
  if (!state.progressName.length) {
    progressName.value.focus();
    $message.warning('请填写流程名称!');
    return;
  }
  templateSave(nodeList, linkList);
};
// 流程保存
const templateSave = async (nodeList, linkList) => {
  await http.post('TemplateProgress/CreateOrUpdate', {
    id: 0,
    progressName: state.progressName,
    progressType: urlParams.value.type,
    processJson: JSON.stringify({ nodeList, linkList })
  });
  $message.success('创建成功!');
  router.push({
    path: 'flow',
    query: { type: urlParams.value.type }
  });
};

// 处理节点编辑
const onSave = val => {
  let nodeList = superFlow.value.graph.nodeList;
  const index = nodeList.findIndex(r => r.meta.id === val.id);
  console.log(nodeList, index);
  nodeList[index].meta = { ...val };
  state.nodeShow = false;
};
// 添加 宽高的
const onAddPx = val => {
  const el = document.querySelector('.flow-super');
  state[val] += el[`offset${val}`];
};
// 处理是否自动连线
const onAutoLine = (id) => {
  // 是否需要自动连线
  if (!state.isAutoLine) return;
  params.linkList.push({
    id: hash(),
    startId: id - 1,
    endId: id,
    startAt: [50, 100],
    endAt: [100, 0],
    meta: null
  });
};

nextTick(() => {
  const el = document.querySelector('.flow-super');
  state.Width = el.offsetWidth;
  state.Height = el.offsetHeight;
});
</script>

<style lang="scss" scoped>
.flow {
  height: 100%;
  width: 100%;
  background-color: #fff;
  position: relative;
  overflow: hidden;
  display: flex;
  flex-direction: column;
  &-super {
    flex: 1;
    overflow: auto;
    position: relative;
    &-bottom {
      position: absolute;
      z-index: 10;
      bottom: 10px;
      left: 50%;
      transform: translateX(-50%);
    }
    &-right {
      position: absolute;
      z-index: 10;
      right: 10px;
      top: 50%;
      transform: translateY(-120%);
    }
  }
}
</style>

<style lang="scss">
.super-flow__node {
  color: #fff;
  border: 0 !important;
  border-radius: 4px;
  overflow: hidden;
  .flow-node {
    height: 100%;
    display: flex;
    flex-direction: column;
    section {
      padding: 10px;
    }
    header {
      border-radius: 5px;
      font-size: 17px;
      height: 40px;
      line-height: 40px;
      padding: 0 12px;
      background-color: var(--el-color-warning);
      text-align: center;
    }
    &-start {
      header {
        background-color: var(--el-color-primary);
      }
    }
    &-end {
      header {
        background-color: var(--el-color-danger);
      }
    }
  }
  .flow-node-input-approval {
    .el-input__wrapper {
      background-color: var(--el-color-warning);
      box-shadow: none;
      border-radius: 0;
      input {
        color: #fff;
      }
    }
  }
}
.super-flow__menu-container {
  .super-flow__menu {
    padding: 0;
    border: 0;
    &-line {
      margin: 0 auto;
      width: 96%;
      display: none;
    }
    &-item {
      padding: 10px;
      width: 100%;
      &:nth-child(even) {
        background-color: #f2f6fc;
      }
      &:hover {
        background-color: #55abfc;
        color: #fff;
      }
      &-icon {
        display: none;
      }
      &-content {
        width: inherit;
        color: inherit;
      }
    }
  }

  .is-disabled {
    display: none;
    background-color: #efefef;

    &:hover {
      background-color: #efefef;
      color: #000;
    }
  }
}
</style>
